#!/usr/bin/python

#* ========================================================================= *#
#* ------------------------------------------------------------------------- *#
##
##  \file		tpl.py
##  \date		Jully 2011
##  \author		TNick
##
##  \brief		Script for generating files from templates
##
##
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
## Please read ReadMe.txt and Licence.txt in root folder @n
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
##
#* ------------------------------------------------------------------------- *#
#* ========================================================================= *#
##
##
##
##
#*  INCLUDES    ------------------------------------------------------------ *#

import	sys
import	os
import	xml.dom.minidom

#*  INCLUDES    ============================================================ *#
##
##
##
##
#*  DATA    ---------------------------------------------------------------- *#

ignore_names = [ ".svn", "tmp", "New Folder", "Untitled"  ]
ignore_termin = [ "~", ".user", ".o", ".obj" ]
ignore_content = [ "build", "html" ]

#*  DATA    ================================================================ *#
##
##
##
##
#*  FUNCTIONS    ----------------------------------------------------------- *#

#* ------------------------------------------------------------------------- *#
def walk( fldr, indent, f__list, p__list, dir__list ):
	'''
	This function simply walks a folder tree and prints its components.

	There may be 3 types of components: folders, file pairs (.h and .cc) and
	single files.
	'''
	fldr = os.path.abspath(fldr)
	files = []
	for file in [file for file in os.listdir(fldr) if not file in [".",".."]]:
		nfile = os.path.join(fldr,file)

		# ignore specific elements
		if ( file in ignore_names ):
			continue

		# ignore elements that end in ...
		b_cont = 0
		for itr_s in ignore_termin:
			if ( file.endswith( itr_s ) ):
				b_cont = 1; break
		if ( b_cont ): continue

		# ignore the vontent but not the item
		if file in ignore_content:
			print(indent + file)
			continue

		# other specific filters
		if ( nfile.endswith( "PythonQt" + os.sep + "trunk" ) ):
			continue

		# type based prcessing
		if os.path.isdir(nfile):
			print(indent + file)
			walk( nfile, indent + "  " )
		else:
			files.append( file )

	files.sort()

	# identify file pairs
	i = 0
	doublets = []
	while ( i < len( files ) ):
		file_n,ext = os.path.splitext( files[i] )
		file_n = file_n.upper()
		ext = ext.upper()
		if ( ext == ".H" ):
			j = 0
			while ( j < len( files ) ):
				file_n_2,ext_2 = os.path.splitext( files[j] )
				file_n_2 = file_n_2.upper()
				ext_2 = ext_2.upper()
				if ( file_n == file_n_2 ):
					if ( ext_2 == ".CC" ):
						doublets.append( os.path.splitext( files[j] )[0] )
						files[i] = ""
						files[j] = ""
						break
				j += 1

		elif ( ext == ".CC" ):
			j = 0
			while ( j < len( files ) ):
				file_n_2,ext_2 = os.path.splitext( files[j] )
				file_n_2 = file_n_2.upper()
				ext_2 = ext_2.upper()
				if ( file_n == file_n_2 ):
					if ( ext_2 == ".H" ):
						doublets.append( os.path.splitext( files[j] )[0] )
						files[i] = ""
						files[j] = ""
						break
				j += 1
		i += 1

	# output file pairs
	for file in doublets:
		print ( indent + file + ".h and " + file + ".cc" )

	# output individual files
	for file in files:
		if ( file != "" ):
			print ( indent + file )

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	printPath( b_path ):
	'''
	Main function for printing a path. This makes use of walk().
	'''
	print( "\n\nPrinting path: " + b_path );
	walk( b_path, "  " )

#* ========================================================================= *#


#* ------------------------------------------------------------------------- *#
def createLists( fldr, f__list, p__list, dir__list ):
	'''
	This only looks at provided path and fills elements to proper list
	(files, pairs or directories). The directories are then recursed.
	'''

	fldr = os.path.abspath(fldr)
	files = []
	for file in [file for file in os.listdir(fldr) if not file in [".",".."]]:
		nfile = os.path.join(fldr,file)

		# ignore specific elements
		if file in ignore_names:
			continue

		# ignore elements that end in ...
		b_cont = 0
		for itr_s in ignore_termin:
			if ( file.endswith( itr_s ) ):
				b_cont = 1; break
		if ( b_cont ): continue

		# ignore the vontent but not the item
		if file in ignore_content:
			dir__list.append( nfile )
			continue

		# other specific filters
		if ( nfile.endswith( "PythonQt" + os.sep + "trunk" ) ):
			continue

		# type based prcessing
		if os.path.isdir(nfile):
			dir__list.append( nfile )
			createLists( nfile, f__list, p__list, dir__list )
		else:
			files.append( nfile )

	files.sort()

	# generate file pairs
	i = 0
	while ( i < len( files ) ):
		file_n,ext = os.path.splitext( files[i] )
		file_n = file_n.upper()
		ext = ext.upper()
		if ( ext == ".H" ):
			j = 0
			while ( j < len( files ) ):
				file_n_2,ext_2 = os.path.splitext( files[j] )
				file_n_2 = file_n_2.upper()
				ext_2 = ext_2.upper()
				if ( file_n == file_n_2 ):
					if ( ext_2 == ".CC" ):
						p__list.append( os.path.splitext( files[j] )[0] )
						files[i] = ""
						files[j] = ""
						break
				j += 1

		elif ( ext == ".CC" ):
			j = 0
			while ( j < len( files ) ):
				file_n_2,ext_2 = os.path.splitext( files[j] )
				file_n_2 = file_n_2.upper()
				ext_2 = ext_2.upper()
				if ( file_n == file_n_2 ):
					if ( ext_2 == ".H" ):
						p__list.append( os.path.splitext( files[j] )[0] )
						files[i] = ""
						files[j] = ""
						break
				j += 1
		i += 1


	files.sort()
	for file in files:
		if ( file != "" ):
			f__list.append( file )

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def getNodeByTitle(node, title):
	'''
	This will look into the kids of the node for one that is named title
	'''
	
	lst_ret = []
	for child in node.childNodes:
		if child.localName=="node":
			if ( child.attributes.getNamedItem( "name" ).nodeValue == title ):
				lst_ret.append( child )
	return lst_ret

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	domRepresentPath( path_string, dom_doc, dom_el ):
	'''
	This function will see if a certain path is represented in the dom tree.

	If it is not, the function will create entire path, with the last element
	being a folder.
	The function returs the element coresponding to this path.
	'''

	# get path components
	the_comp = path_string.split( os.sep )

	# create / open each in turn
	crt_dom_el = dom_el
	i = 0
	if ( len( the_comp[0] ) == 0 ):
		i = 1
	while ( i < len( the_comp ) ):
		dom_el_h = getNodeByTitle( crt_dom_el, the_comp[i] )
		if ( len( dom_el_h ) == 0 ):
			dom_el_h.append( dom_doc.createElement( "node" ) )
			crt_dom_el.appendChild( dom_el_h[0] )
			dom_el_h[0].setAttribute( 'name', the_comp[i] )
			dom_el_h[0].setAttribute( 'type', 'dir' )
		crt_dom_el = dom_el_h[0]
		i += 1

	return crt_dom_el
	# we don't know if last one is a file or a folder but the caller should
	# change if not folder


#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	readFilesDoxygen( b_path ):
	'''
	The function opens the files.html generated by doxygen from a project
	and extracts information

	The function returns a list consisting of:
	0 - the name of the generated file
	1 - file name
	2 - link to generated source
	3 - description
	4 - associated elements defined inside
	'''

	lst_ret = []

	f = open(b_path, 'r')
	f_content = f.read().split("\n")
	f.close()
	i = 0
	while ( i < len( f_content ) ):
		f_content[i] = f_content[i].strip()
		i += 1

	ln_marker = "<tr><td class=\"indexkey\"><a class=\"el\" href=\""
	ln_m2 = "\">"
	ln_m3 = "</a> <a href=\""
	ln_m4 = "\">[code]</a></td><td class=\"indexvalue\">"
	ln_m5 = "</td></tr>"
	ln_m6 = "<a class=\"el\" href="
	ln_m7 = "Contains the implementation of "
	ln_m8 = "Contains the definition for "

	for ln in f_content:
		if ( ln.startswith( ln_marker ) ):
			ln_cont = ["", "", "", "", ""]
			ln = ln[ len( ln_marker ) :]

			idx = ln.find( ln_m2 )
			ln_cont[0] = ln[:idx]
			ln = ln[idx + len(ln_m2):]

			idx = ln.find( ln_m3 )
			ln_cont[1] = ln[:idx]
			ln = ln[idx + len(ln_m3):]

			idx = ln.find( ln_m4 )
			ln_cont[2] = ln[:idx]
			ln = ln[idx + len(ln_m4):]

			idx = ln.find( ln_m5 )
			ln_cont[3] = ln[:idx]

			idx = ln_cont[3].find( ln_m6 )
			ln = ""
			while ( idx != -1 ):
				ln = ln_cont[3][:idx]
				ln_cont[3] = ln_cont[3][idx+len(ln_m6):]

				idx = ln_cont[3].find( ">" )
				ln_cont[3] = ln_cont[3][idx+1:]

				idx = ln_cont[3].find( "</a>" )
				ln_cont[4] += ln_cont[3][:idx] + " "
				ln_cont[3] = ln + ln_cont[3][:idx] + ln_cont[3][idx+4:]

				idx = ln_cont[3].find( ln_m6 )


			if ( ln_cont[3].startswith( ln_m7 ) ):
				idx_S = len( ln_m7 )
			elif ( ln_cont[3].startswith( ln_m8 ) ):
				idx_S = len( ln_m8 )
			else:
				lst_ret.append( ln_cont )
				continue

			idx = ln_cont[3].find( " class" )
			if ( idx != -1 ):
				ln_cont[4] += ln_cont[3][idx_S:idx] + " "
			else:
				ln_cont[4] += ln_cont[3][idx_S:] + " "


			ln_cont[3] = ln_cont[3].strip()
			lst_ret.append( ln_cont )

	return lst_ret

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	locateDoxyFileInf( file_name, list_of_df ):
	'''
	Looks into the list at index 1 to find file_name

	returns the index where it was found or -1
	'''

	i = 0
	while ( i < len( list_of_df ) ):
		if ( file_name == list_of_df[i][1] ):
			return i
		i += 1
	return -1

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def saveAditInfo ( fl, dxy_fl, thedoc, dm_el):

	idx = locateDoxyFileInf ( fl, dxy_fl )
	if ( idx != -1 ):
		if ( len( dxy_fl[idx][0] ) > 0 ):
			dm_el.setAttribute( 'generated', dxy_fl[idx][0] )
		if ( len( dxy_fl[idx][2] ) > 0 ):
			dm_el.setAttribute( 'gen_src',   dxy_fl[idx][2] )
		if ( len( dxy_fl[idx][4] ) > 0 ):
			dm_el.setAttribute( 'assoc_el',  dxy_fl[idx][4] )
		dxy_fl[idx][3] = dxy_fl[idx][3].strip()
		if ( len( dxy_fl[idx][3] ) > 0 ):
			prev_tx = "".join(t.nodeValue \
				for t in dm_el.childNodes if t.nodeType == t.TEXT_NODE)
			prev_tx = prev_tx.strip()
			# only add text if it is not already there
			if ( prev_tx.find( dxy_fl[idx][3] ) == -1 ):
				print("Previous text is: <" + prev_tx + ">")
				print("New text is: <" + dxy_fl[idx][3] + ">")
				print("....................................")
				text = thedoc.createTextNode( dxy_fl[idx][3] )
				dm_el.appendChild(text)

	# 0 - the name of the generated file
	# 1 - file name
	# 2 - link to generated source
	# 3 - description
	# 4 - associated elements defined inside

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def generateDocNode( node_parent, level, out_file ):
	'''
	Called for each node in FileDatabase.xml; this may be file, pair or folder

	node_parent is the dom element that we're generating now
	level is the depth in the tree (0 is topmost)
	out_file is an object that supports .write() (like a file)
	'''

	#from pprint import pprint
	#pprint (vars(node_parent))


	if ( len( node_parent.attributes ) == 0 ):
		# recursive printing of child nodes
		for t in node_parent.childNodes:
			if ( t.nodeType == t.ELEMENT_NODE ):
				if ( t.tagName == "node" ):
					generateDocNode( t, level + 1, out_file )
		return

	out_file.write( "\t<tr>\n" )
	indent = "&nbsp;&nbsp;&nbsp;" * level
	try:

		if ( node_parent.attributes["type"].value == "dir" ):
			tx_lbl = "<b>" + node_parent.attributes["name"].value + "</b>"
		elif ( node_parent.attributes["type"].value == "pair" ):
			tx_lbl = node_parent.attributes["name"].value + ".h and "  + \
					 node_parent.attributes["name"].value + ".cc"
		else:	# "file"
			tx_lbl = node_parent.attributes["name"].value

		asoc_v = ""
		try:
			asoc_v = node_parent.attributes["assoc_el"].value
		except Exception, e:
			pass
		finally:
			pass

		descr = ""
		try:
			descr = "".join(t.nodeValue \
				for t in node_parent.childNodes \
				if t.nodeType == t.TEXT_NODE)
		except Exception, e:
			pass
		finally:
			pass
		d_out = ""
		for line_descr in descr.split( "\n" ):
			line_descr = line_descr.strip()
			if ( len( line_descr ) > 0 ):
				if ( len( d_out ) > 0 ):
					d_out += "<br>" + line_descr
				else:
					d_out = line_descr

		# file name (names)
		out_file.write( \
			"\t\t<td>" + \
			indent + \
			tx_lbl + \
			"</td>\n")

		# associated class
		out_file.write( \
			"\t\t<td>&nbsp;" + \
			asoc_v + \
			"</td>\n")

		# description oif the file
		out_file.write( \
			"\t\t<td>&nbsp;" + \
			d_out + \
			"</td>\n")


		out_file.write( "\n" )

		if ( node_parent.attributes["type"].value == "dir" ):
			# recursive printing of child nodes
			for t in node_parent.childNodes:
				if ( t.nodeType == t.ELEMENT_NODE ):
					if ( t.tagName == "node" ):
						generateDocNode( t, level + 1, out_file )

		else:
			pass
	except KeyError:
		print("key error")
	finally:
		pass

	out_file.write( "\t</tr>\n" )


#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	updateFileDatabase( b_path ):
	'''
	scan the file tree and update the database
	
	b_path points to the trunk folder
	'''
	

	print( "\n\nParsing path: " + b_path );
	f__list = []
	p__list = []
	dir__list = []
	createLists( b_path, f__list, p__list, dir__list )
	print( "    File system tree scanned. " + \
			str( len(dir__list) )  + " folders, " + \
			str( len(p__list) )  + " pairs and " + \
			str( len(f__list) )  + " files." );


	# open the database
	impl = xml.dom.minidom.getDOMImplementation()
	f_dbf = os.path.abspath( b_path + "/source/doc/FileDatabase.xml" )
	if ( os.path.exists( f_dbf ) ):
		b_new = 0
		f = open(f_dbf, 'r')
		f_content = f.read()
		f.close()
		f_content.replace("\n", "")
		thedoc = xml.dom.minidom.parseString( f_content )
		print( "    Database at " + f_dbf + " was opened" )
		dom_root = thedoc.documentElement
		assert dom_root.tagName == "FileDatabase"
	else:
		b_new = 1
		thedoc = impl.createDocument(None, "FileDatabase", None)
		dom_root = thedoc.documentElement
		print( "    Database at " + f_dbf + " was created" )


	# the tree
	dom_tree = dom_root.getElementsByTagName("tree")
	if ( len( dom_tree ) == 0 ):
		dom_tree.append( thedoc.createElement( "tree" ) )
		dom_root.appendChild( dom_tree[0] )

	# extract informations from files.html
	dxy_fl = []
	str_files = b_path + "/doc/html/files.html"
	if ( os.path.exists( str_files ) ):
		print( "    Get doxygen generated info for files" )
		dxy_fl = readFilesDoxygen( str_files )


	print( "    Updating the tree" )

	# update the folders
	for l_el in dir__list:
		dm_el = domRepresentPath( l_el.replace( b_path, ""), thedoc, dom_tree[0] )
		saveAditInfo( os.path.basename( l_el ), dxy_fl, thedoc, dm_el )

	# update the file pairs
	for l_el in p__list:
		dm_el = domRepresentPath( l_el.replace( b_path, ""), thedoc, dom_tree[0] )
		dm_el.setAttribute( 'type', 'pair' )
		saveAditInfo( os.path.basename( l_el ) + ".h", dxy_fl, thedoc, dm_el )

	# update the single files
	for l_el in f__list:
		dm_el = domRepresentPath( l_el.replace( b_path, ""), thedoc, dom_tree[0] )
		dm_el.setAttribute( 'type', 'file' )

		saveAditInfo( os.path.basename( l_el ), dxy_fl, thedoc, dm_el )


	# save the database
	print( "    Saving database" )
	f = open( f_dbf, "w" )
	try:
		f_content = thedoc.toprettyxml( indent="    " )
		f_cont_l = f_content.split("\n")
		f_content = ""
		i = 0
		while ( i < len( f_cont_l ) ):
			if ( len ( f_cont_l[i].strip() ) != 0 ):
				f_content += f_cont_l[i] + "\n"
			i += 1
		# ugly to look at-> thedoc.writexml( f, encoding= 'utf-8' )

		f.write( f_content )
	finally:
		f.close()

	thedoc.unlink()


#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def generateDocFile( b_path ):
	'''
	Reads FileDatabase.xml and generates the documentation file based on it

	returns either 1 for succes or 0 for failure
	b_path points to the trunk folder
	'''

	print( "Generating .dox file from database" )

	f_dbf = os.path.abspath( b_path + "/source/doc/FileDatabase.xml" )
	f_tpl = os.path.abspath( os.path.dirname(__file__) )
	f_tpl = os.path.abspath( f_tpl + "/../templ/doc_file_list_template.txt" )
	f_out = os.path.abspath( b_path + "/source/doc/file_index.dox" )


	# open the database
	if ( os.path.exists( f_dbf ) ):
		b_new = 0
		f = open(f_dbf, 'r')
		f_content = f.read()
		f.close()
		f_content.replace("\n", "")
		thedoc = xml.dom.minidom.parseString( f_content )
		print( "    Database at " + f_dbf + " was opened" )
		dom_root = thedoc.documentElement
		assert dom_root.tagName == "FileDatabase"
	else:
		print("     Could not open " + f_dbf +" database" )
		return 0

	# read template for file
	idx_tpl = -1
	mark_tpl = "##XML_CONTENT##"
	if ( os.path.exists( f_tpl ) ):
		b_new = 0
		f = open(f_tpl, 'r')
		tpl_content = f.read()
		f.close()

		idx_tpl = tpl_content.find( mark_tpl )

		print( "    Template at " + f_tpl + " was readed" )
	else:
		print("     Could not open " + f_dbf +" template file" )
		return 0


	# create the file
	fou = open( f_out, "w" )
	try:
		# write first part of the template
		fou.write( tpl_content[:idx_tpl] )

		# prepare for input
		fou.write( "<table cellpadding =\"0\" cellspacing=\"\" align=\"center\" width=\"100%\" bgcolor=\"#E0FFE0\" border=\"1\">\n" )

		# generate dynamic values
		for t in dom_root.childNodes:
			if ( t.nodeType == t.ELEMENT_NODE ):
				if ( t.tagName == "tree" ):
					generateDocNode( t, 0, fou )

		# finalise custom input
		fou.write( "</table>\n" )

		# write second part of the template
		fou.write( tpl_content[idx_tpl+len(mark_tpl):] )

	finally:
		fou.close()



	return 1

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def	main():
	

	if (len(sys.argv) > 1):
		i = 1
		while (i < len( sys.argv ) ):
			updateFileDatabase( sys.argv[i] )
			generateDocFile( sys.argv[i] )
			i = i + 1

	else:
		base_pth = os.getcwd()
		base_pth = os.path.abspath( base_pth + "/../.." )

		updateFileDatabase( base_pth )
		generateDocFile( base_pth )


	return 0
#* ========================================================================= *#


if __name__ == "__main__":
	sys.exit( main() )

#*  FUNCTIONS    =========================================================== *#
##
##
##
##
#* ------------------------------------------------------------------------- *#
#* ========================================================================= *#
